var $prefetchCount = 0;
var $failures = 0;
- var $maxFailures = 200;
+ var $maxFailures = 5;
+ var $failedTextRetrievals = 0;
+ var $maxConsecutiveFailedTextRetrievals = 200;
var $failureTimeout = 5; // Seconds to sleep after db failure
var $php = "php";
}
private function doGetText( $id ) {
- if ( $this->spawn ) {
- return $this->getTextSpawned( $id );
- } else {
- return $this->getTextDbSafe( $id );
+
+ $this->failures = 0;
+ $ex = new MWException( "Graceful storage failure" );
+ while (true) {
+ if ( $this->spawn ) {
+ if ($this->failures) {
+ // we don't know why it failed, could be the child process
+ // borked, could be db entry busted, could be db server out to lunch,
+ // so cover all bases
+ $this->closeSpawn();
+ $this->openSpawn();
+ }
+ $text = $this->getTextSpawned( $id );
+ } else {
+ $text = $this->getTextDbSafe( $id );
+ }
+ if ( $text === false ) {
+ $this->failures++;
+ if ( $this->failures > $this->maxFailures) {
+ $this->progress( "Failed to retrieve revision text for text id ".
+ "$id after $this->maxFailures tries, giving up" );
+ // were there so many bad retrievals in a row we want to bail?
+ // at some point we have to declare the dump irretrievably broken
+ $this->failedTextRetrievals++;
+ if ($this->failedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals) {
+ throw $ex;
+ }
+ else {
+ // would be nice to return something better to the caller someday,
+ // log what we know about the failure and about the revision
+ return("");
+ }
+ } else {
+ $this->progress( "Error $this->failures " .
+ "of allowed $this->maxFailures retrieving revision text for text id $id! " .
+ "Pausing $this->failureTimeout seconds before retry..." );
+ sleep( $this->failureTimeout );
+ }
+ } else {
+ $this->failedTextRetrievals= 0;
+ return( $text );
+ }
}
+
}
/**
} catch ( DBQueryError $ex ) {
$text = false;
}
- if ( $text === false ) {
- $this->failures++;
- if ( $this->failures > $this->maxFailures ) {
- throw $ex;
- } else {
- $this->progress( "Database failure $this->failures " .
- "of allowed $this->maxFailures for revision $id! " .
- "Pausing $this->failureTimeout seconds..." );
- sleep( $this->failureTimeout );
- }
- } else {
- return $text;
- }
+ return $text;
}
}
// First time?
$this->openSpawn();
}
- while ( true ) {
-
- $text = $this->getTextSpawnedOnce( $id );
- if ( !is_string( $text ) ) {
- $this->progress( "Database subprocess failed. Respawning..." );
-
- $this->closeSpawn();
- sleep( $this->failureTimeout );
- $this->openSpawn();
-
- continue;
- }
- wfRestoreWarnings();
- return $text;
- }
+ $text = $this->getTextSpawnedOnce( $id );
+ wfRestoreWarnings();
+ return $text;
}
function openSpawn() {
if ( $len === false ) return false;
$nbytes = intval( $len );
+ // actual error, not zero-length text
+ if ($nbytes < 0 ) return false;
+
$text = "";
// Subprocess may not send everything at once, we have to loop.
$this->mDescription = "Fetch the revision text from an old_id";
}
- public function execute() {
+ /*
+ * returns a string containing the following in order:
+ * textid
+ * \n
+ * length of text (-1 on error = failure to retrieve/unserialize/gunzip/etc)
+ * \n
+ * text (may be empty)
+ *
+ * note that that the text string itself is *not* followed by newline
+ */
+ public function execute() {
$db = wfGetDB( DB_SLAVE );
$stdin = $this->getStdin();
while ( !feof( $stdin ) ) {
}
$textId = intval( $line );
$text = $this->doGetText( $db, $textId );
- $this->output( $textId . "\n" . strlen( $text ) . "\n" . $text );
+ if ($text === false) {
+ # actual error, not zero-length text
+ $textLen = "-1";
+ }
+ else {
+ $textLen = strlen($text);
+ }
+ $this->output( $textId . "\n" . $textLen . "\n" . $text );
}
}